package logic;

import gui.ConsoleGUI;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;

public class ChessGame implements Runnable {

	private int gameState = GAME_STATE_GREEN;
	public static final int GAME_STATE_RED = 0;
	public static final int GAME_STATE_GREEN = 1;
	public static final int GAME_STATE_END_GREEN_WON = 2;
	public static final int GAME_STATE_END_RED_WON = 3;
	private IPlayerHandler greenPlayerHandler;
	private IPlayerHandler redPlayerHandler;
	private IPlayerHandler activePlayerHandler;
	private List<Piece> pieces = new ArrayList<Piece>();
	private List<Piece> capturedPieces = new ArrayList<Piece>();
	private MoveValidator moveValidator;

	public ChessGame() {
		this.moveValidator = new MoveValidator(this);

		// create and place piece
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_ROOK, Piece.ROW_1,
				Piece.COLUMN_A);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_HOURSE, Piece.ROW_1,
				Piece.COLUMN_B);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_ELEPHANT,
				Piece.ROW_1, Piece.COLUMN_C);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_GUARD, Piece.ROW_1,
				Piece.COLUMN_D);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_KING, Piece.ROW_1,
				Piece.COLUMN_E);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_GUARD, Piece.ROW_1,
				Piece.COLUMN_F);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_ELEPHANT,
				Piece.ROW_1, Piece.COLUMN_G);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_HOURSE, Piece.ROW_1,
				Piece.COLUMN_H);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_ROOK, Piece.ROW_1,
				Piece.COLUMN_I);

		// pawn
		int currentColumn = Piece.COLUMN_A;
		for (int i = 0; i < 5; i++) {
			createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_PAWN,
					Piece.ROW_4, currentColumn);
			currentColumn += 2;
		}
		// cannon
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_CANNON, Piece.ROW_3,
				Piece.COLUMN_B);
		createAndPlacePiece(Piece.COLOR_GREEN, Piece.TYPE_CANNON, Piece.ROW_3,
				Piece.COLUMN_H);

		// create and place piece
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_ROOK, Piece.ROW_10,
				Piece.COLUMN_A);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_HOURSE, Piece.ROW_10,
				Piece.COLUMN_B);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_ELEPHANT, Piece.ROW_10,
				Piece.COLUMN_C);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_GUARD, Piece.ROW_10,
				Piece.COLUMN_D);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_KING, Piece.ROW_10,
				Piece.COLUMN_E);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_GUARD, Piece.ROW_10,
				Piece.COLUMN_F);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_ELEPHANT, Piece.ROW_10,
				Piece.COLUMN_G);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_HOURSE, Piece.ROW_10,
				Piece.COLUMN_H);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_ROOK, Piece.ROW_10,
				Piece.COLUMN_I);

		// pawn
		currentColumn = Piece.COLUMN_A;
		for (int i = 0; i < 5; i++) {
			createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_PAWN, Piece.ROW_7,
					currentColumn);
			currentColumn += 2;
		}
		// cannon
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_CANNON, Piece.ROW_8,
				Piece.COLUMN_B);
		createAndPlacePiece(Piece.COLOR_RED, Piece.TYPE_CANNON, Piece.ROW_8,
				Piece.COLUMN_H);
	}

	/**
	 * create piece instance and add it to the internal list of pieces
	 * 
	 * @param color
	 * @param type
	 * @param row
	 * @param column
	 */
	private void createAndPlacePiece(int color, int type, int row, int column) {
		Piece piece = new Piece(color, type, row, column);
		this.pieces.add(piece);
	}

	public boolean movePiece(Move move) {
		move.setCapturedPiece(getNonCapturedPieceAtLocation(
				move.getTargetRow(), move.getTargetColumn()));
		// ConsoleGUI.printCurrentGameState(this);
		// System.out.println(move);
		// System.out.println(move.capturedPiece);
		Piece piece = getNonCapturedPieceAtLocation(move.getSourceRow(),
				move.getSourceColumn());
		System.out.println("move: " + move);
		System.out.println("captured piece: " + move.getCapturedPiece());
		System.out.println("source piece: " + piece);
		// System.out.println(piece);
		if (piece == null)
			return false;
		// check if the move is capturing an opponent piece
		int opponentColor = (piece.getColor() == Piece.COLOR_GREEN ? Piece.COLOR_RED
				: Piece.COLOR_GREEN);
		if (isNonCapturedPieceAtLocation(opponentColor, move.getTargetRow(),
				move.getTargetColumn())) {
			Piece opponentPiece = getNonCapturedPieceAtLocation(
					move.getTargetRow(), move.getTargetColumn());
			System.out.println("captured piece 1: " + opponentPiece);
			this.pieces.remove(opponentPiece);
			this.capturedPieces.add(opponentPiece);
			opponentPiece.isCaptured(true);
		}

		if (piece.getType() == Piece.TYPE_PAWN) {
			if (this.gameState == GAME_STATE_GREEN
					&& move.getTargetRow() >= Piece.ROW_6)
				piece.passRiver(true);
			else if (this.gameState == GAME_STATE_RED
					&& move.getTargetRow() <= Piece.ROW_5)
				piece.passRiver(true);
		}
		piece.setRow(move.getTargetRow());
		piece.setColumn(move.getTargetColumn());

		// this.changeGameState();
		return true;
	}

	public void changeGameState() {
		// check if game end condition has been reached
		if (this.isGameEndConditionReached()) {
			if (this.gameState == ChessGame.GAME_STATE_GREEN) {
				System.out.println("Game over!, green won");
			} else if (this.gameState == ChessGame.GAME_STATE_RED) {
				System.out.println("Game over!, red won");
			} else {
				// this.gameState = ChessGame.GAME_STATE_END;
			}
			return;
		}
		switch (this.gameState) {
		case GAME_STATE_RED:
			this.gameState = GAME_STATE_GREEN;
			break;
		case GAME_STATE_GREEN:
			this.gameState = GAME_STATE_RED;
			break;
		case GAME_STATE_END_GREEN_WON:
			break;
		case GAME_STATE_END_RED_WON:
			break;
		default:
			throw new IllegalStateException("unknown game state:"
					+ this.gameState);
		}

	}

	/**
	 * checks whether there is a piece at the specified location that is not
	 * marked as 'captured' and has the specified color.
	 * 
	 * @param color
	 * @param row
	 * @param column
	 * @return true, if the location contains a not-captured piece of the
	 *         specified color
	 */
	public boolean isNonCapturedPieceAtLocation(int color, int row, int column) {
		for (Piece piece : this.pieces) {
			if (piece.getRow() == row && piece.getColumn() == column
					&& piece.getColor() == color) {
				return true;
			}
		}
		return false;
	}

	/**
	 * check if the games end condition is met: one color has a captured king
	 * 
	 * @return true if the game end condition is met
	 */
	private boolean isGameEndConditionReached() {

		// for (Piece piece : this.pieces) {
		// if (piece.getType() == Piece.TYPE_KING && piece.isCaptured()) {
		// return true;
		// } else {
		//
		// }
		// }
		// return false;
		for (Piece piece : capturedPieces) {
			if (piece.getType() == Piece.TYPE_KING)
				return true;
		}
		return false;
	}

	/**
	 * returns the first piece at the specified location that is not marked as
	 * 'captured'.
	 * 
	 * @param row
	 * @param column
	 * @return
	 */
	public Piece getNonCapturedPieceAtLocation(int row, int column) {
		for (Piece piece : this.pieces) {
			if (piece.getRow() == row && piece.getColumn() == column) {
				return piece;
			}
		}
		return null;
	}

	public int getGameState() {
		return gameState;
	}

	public String showGameState() {
		if (gameState == GAME_STATE_END_GREEN_WON) {
			return "GAME END, GREEN WON";
		} else if (gameState == GAME_STATE_GREEN) {
			return "GREEN TURN";
		} else if (gameState == GAME_STATE_RED) {
			return "RED TURN";
		} else if (gameState == GAME_STATE_END_RED_WON) {
			return "GAME END, RED WON";
		} else {
			return "UNKNOWN";
		}

	}

	/**
	 * Checks whether there is a non-captured piece at the specified location
	 * 
	 * @param currentRow
	 * @param currentColumn
	 * @return
	 */
	public boolean isNonCapturedPieceAtLocation(int currentRow,
			int currentColumn) {
		for (Piece piece : pieces) {
			if (piece.getRow() == currentRow
					&& piece.getColumn() == currentColumn) {
				return true;
			}
		}
		return false;
	}

	@Override
	public void run() {
		this.startGame();
	}

	/**
	 * set the player/client for the specified piece color
	 * 
	 * @param pieceColor
	 *            - the color the client/player controls
	 * @param playerHandler
	 *            - the player/client
	 */
	public void setPlayer(int pieceColor, IPlayerHandler playerHandler) {
		switch (pieceColor) {
		case Piece.COLOR_GREEN:
			this.greenPlayerHandler = playerHandler;
			break;
		case Piece.COLOR_RED:
			this.redPlayerHandler = playerHandler;
			break;
		default:
			throw new IllegalArgumentException("Invalid pieceColor: "
					+ pieceColor);
		}
	}

	/**
	 * start main game flow
	 */
	public void startGame() {

		// check if all players are ready
		System.out.println("chessgame: waiting for players");
		while (this.redPlayerHandler == null || this.greenPlayerHandler == null) {
			// players are still missing
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO: handle exception
			}
		}

		// set start player
		this.activePlayerHandler = this.greenPlayerHandler;

		// start game flow
		System.out.println("chessgame: starting game flow");
		// activePlayerHandler.moveSuccessfullyExecuted(null);
		while (!isGameEndConditionReached()) {
			if (this.gameState == GAME_STATE_RED) {
				Log.getInstance().log("logyou|Thinking...");
			} else if (this.gameState == GAME_STATE_GREEN) {
				Log.getInstance().log("logai|Thinking...");
			}
			waitForMove();
			// System.out.println("move done");
			ConsoleGUI.printCurrentGameState(this);
			swapActivePlayer();
			Log.getInstance().log("gamestate|" + this.showGameState());
			// System.out.println(activePlayerHandler);
		}
		ConsoleGUI.printCurrentGameState(this);
		System.out.println("chessgame: game ended");
	}

	/**
	 * wait for a valid player move and execute it
	 */
	private void waitForMove() {

		Move move = null;
		// wait for a valid move
		do {
			move = this.activePlayerHandler.getMove();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
			if (move != null && this.moveValidator.isMoveValid(move, false)) {
				break;
			} else if (move != null
					&& !this.moveValidator.isMoveValid(move, false)) {
				System.out.println("provived move was invalid");
				ConsoleGUI.printCurrentGameState(this);
				move = null;
			}
		} while (move == null);

		System.out.println("move done");
		// execute move
		boolean success = this.movePiece(move);

		// log
		if (this.gameState == GAME_STATE_RED) {
			Log.getInstance().log("logyou|move: " + move);
		} else if (this.gameState == GAME_STATE_GREEN) {
			Log.getInstance().log("logai|move: " + move);
		}

		if (success) {
			this.redPlayerHandler.moveSuccessfullyExecuted(move);
			this.greenPlayerHandler.moveSuccessfullyExecuted(move);
		} else {
			throw new IllegalStateException(
					"move was valid, but failed to execute");
		}
		System.out.println(isGameEndConditionReached());
		if (isGameEndConditionReached()) {
			System.exit(3);
			if (this.gameState == ChessGame.GAME_STATE_GREEN) {
				System.out.println("green won");
			} else {
				System.out.println("red won");
			}
		}
	}

	/**
	 * swap active player and update game state
	 */
	private void swapActivePlayer() {

		if (this.activePlayerHandler == this.redPlayerHandler) {
			// System.out.println(activePlayerHandler);
			this.activePlayerHandler = this.greenPlayerHandler;
		} else {
			// System.out.println(activePlayerHandler);
			this.activePlayerHandler = this.redPlayerHandler;
		}
		// System.out.println(activePlayerHandler);
		this.changeGameState();
		// System.out.println(this.showGameState());
	}

	public List<Piece> getPieces() {
		// TODO Auto-generated method stub
		return this.pieces;
	}

	public MoveValidator getMoveValidator() {
		return this.moveValidator;
	}

	public void undoMove(Move move) {
		Piece piece = getNonCapturedPieceAtLocation(move.getTargetRow(),
				move.getTargetColumn());
		// System.out.println("target :" + piece);
		// System.out.println("source :"
		// + this.getNonCapturedPieceAtLocation(move.getSourceRow(),
		// move.getSourceColumn()));
		// System.out.println(move.getCapturedPiece());
		// ConsoleGUI.printCurrentGameState(this);
		// System.out.println(move);
		// System.out.println("undo move: " + piece);
		if (piece.getType() == Piece.TYPE_PAWN && piece.passRiver()) {
			if (piece.getColor() == Piece.COLOR_GREEN
					&& move.getSourceRow() == Piece.ROW_5) {
				piece.passRiver(false);
			} else if (piece.getColor() == Piece.COLOR_RED
					&& move.getSourceRow() == Piece.ROW_6) {
				piece.passRiver(false);
			}
		}
		piece.setRow(move.getSourceRow());
		piece.setColumn(move.getSourceColumn());
		if (move.getCapturedPiece() != null) {
			move.getCapturedPiece().setRow(move.getTargetRow());
			move.getCapturedPiece().setColumn(move.getTargetColumn());
			move.getCapturedPiece().isCaptured(false);
			this.capturedPieces.remove(move.getCapturedPiece());
			this.pieces.add(move.getCapturedPiece());
		}
		if (piece.getColor() == Piece.COLOR_GREEN) {
			this.gameState = GAME_STATE_GREEN;
		} else {
			this.gameState = GAME_STATE_RED;
		}
		System.out.println("piece undo move: " + piece);
	}
}
